import config from "./config";
import * as TextEncoding from "./TextEncoding";
import Common from "./common.js";
const { sendKey , mKey } = config;

export default class Client extends Common {
  UDP;
  pageContext;
  userInfo;
  heartbeat;
  maxReconnects = 2; // 最大心跳重连次数
  maxRetries = 2; // 最大重试次数
  retryTimeout = 2000; // 操作重试超时时间(ms)

  messageQueue = []; // 消息队列
  pendingMessages = new Map(); // 待确认的消息
  lastHeartbeat = 0; // 最后一次心跳时间
  reconnectTimes = 0; // 重连次数
  isLine = false;  // 是否在线

  constructor(parameters) {
    super(parameters);
    this.pageContext = parameters;
    this.create();
  }

  // 初始化 创建房间
  create() {
    this.UDP = wx.createUDPSocket();
    this.UDP.bind(config.clientPort);
    this.onMessage();
    this.userInfo = wx.getStorageSync("userInfo");
  }

  // 清除定时器
  clearnTimers() {
    clearInterval(this.heartbeat);
    this.heartbeat = null;
  }

  // 发送消息 （只发1次）
  onceSend(
    message = {},
    address = this.gameMessage.gameIp,
    port = config.servePort
  ) {
    this._sendMessage(message, address, port);
  }

  // 发送消息（会尝试重发）
  send(message = {}, address = this.gameMessage.gameIp, port = config.servePort) {
    const messageId = this.generateMessageId();
    const wrappedMessage = {
      [mKey.isNeedReply]: 1,
      ...message,
      [mKey.messageId]: messageId,
    };

    // 添加到待确认队列
    this.pendingMessages.set(messageId, {
      message: wrappedMessage,
      address,
      port,
      retries: 0,
    });

    this._sendMessage(wrappedMessage, address, port);
    this._startMessageRetry(messageId);
  }
  // 实际发送消息
  _sendMessage(message, address = config.broadcast, port = config.servePort) {
    // loading
    if(message[mKey.loadingText]){
      if(this.loadingText){
        if(this.loadingText !== message[mKey.loadingText]){
          wx.hideLoading();
          wx.showLoading({
            title: message[mKey.loadingText],
            mask: true,
          });
        }
      } else {
        this.loadingText = message[mKey.loadingText];
        wx.showLoading({
          title: message[mKey.loadingText],
          mask: true,
        });
      }
    }

    const wrappedMessage = {
      ...message,
    };
    Object.keys(wrappedMessage).forEach((key) => {
      if (wrappedMessage[key] === null || wrappedMessage[key] === "" || wrappedMessage[key] === "undefined") {
        delete wrappedMessage[key];
      }
    });
    delete wrappedMessage[mKey.loadingText];
    delete wrappedMessage[mKey.errorMessage];
    delete wrappedMessage[mKey.jumpMessage];
    delete wrappedMessage[mKey.failJumpTo];
    wrappedMessage[mKey.isNeedReply] === 0 && delete wrappedMessage[mKey.isNeedReply];
    this.UDP && this.UDP.send({
        address,
        message: JSON.stringify(wrappedMessage),
        port,
    });
  }

  // 启动消息重试机制
  _startMessageRetry(messageId) {
    setTimeout(() => {
      const pending = this.pendingMessages.get(messageId);
      if (pending) {
        if (pending.retries < this.maxRetries) {
          // 尝试
          pending.retries++;
          this.pendingMessages.set(messageId, pending);
          this._sendMessage(pending.message, pending.address, pending.port);
          this._startMessageRetry(messageId);
        } else if (pending.retries === this.maxRetries) {
          // 重启
          this.resetUDP([5, 10, 11]);
          pending.retries++;
          this.pendingMessages.set(messageId, pending);
          this._sendMessage(pending.message, pending.address, pending.port);
          this._startMessageRetry(messageId);
        } else {
          // 操作失败 请重试
          wx.hideLoading();
          this.pendingMessages.delete(messageId);
          this.loadingText = undefined;
          // 提示语
          if (pending.message[mKey.errorMessage]) {
            wx.showToast({
              title: pending.message[mKey.errorMessage],
              icon: "none",
              mask: true,
              duration: 3000,
            });
          }
          // 跳转后提示
          if (pending.message[mKey.jumpMessage]) {
            getApp().globalData.showToastTitle = pending.message[mKey.jumpMessage];
          }
          // 跳转页面
          if (pending.message[mKey.failJumpTo]) {
            if (pending.message[mKey.errorMessage]) {
              setTimeout(() => {
                this.pageContext.destroy();
              }, 3000);
            } else {
              this.pageContext.destroy();
            }
          }
        }
      }
    }, this.retryTimeout);
  }

  // 退出房间(手动)
  exitRoomOk() {
    this.send(
      {
        [mKey.messageType]: sendKey.client_exit_room,
        [mKey.failJumpTo]: 1,
        [mKey.loadingText]: "正在退出房间",
      },
      this.gameMessage.gameIp
    );
  }

  // 退出房间(直接)
  exitRoom() {
    this.send(
      {
        [mKey.messageType]: sendKey.client_exit_room,
      },
      this.gameMessage.gameIp
    );
  }

  // 玩家切出(离线)
  onLeave() {
    this.isLine = false;
    this.onceSend(
      { [mKey.messageType]: sendKey.client_leave_room },
      this.gameMessage.gameIp
    );
    this.resetUDP([0, 1, 2, 9, 10, 11]);
  }

  // 心跳机制（加入房间后启动）
  startHeartbeat() {
    this.clearnTimers();
    this.heartbeat = setInterval(() => {
      if (this.UDP && this.gameMessage.gameIp && this.heartbeat) {
        // 检查心跳超时
        if (this.lastHeartbeat && Date.now() - this.lastHeartbeat > 6000) {
          // if (this.reconnectTimes < this.maxReconnects) {
          //   this.reconnectTimes++;
          //   this.resetUDP([4, 7, 10, 11]);
          //   this.onceSend({},this.gameMessage.gameIp);
          //   this.startHeartbeat();
          // } else {
            // 告诉服务端  超时
            this.onLeave();
            this.pageContext.setData({modalCole: 8})
          // }
        }
      } else {
        this.clearnTimers();
      }
    }, 5000);
  }

  // 网络断开退出
  networkOut(){
    this.onceSend(
      {
        [mKey.messageType]: sendKey.client_exit_room,
      },
      this.gameMessage.gameIp
    );
    this.pageContext.destroy();
  }
  // 网络断开重连
  networkTry(){
    // 重新加载
    this.resetUDP([10, 11]);
    this.clientTryConnect();
  }

  // 接收消息
  onMessage() {
    this.UDP.onMessage((e) => {
      const decoder = new TextEncoding.TextDecoder("utf-8");
      const message = JSON.parse(decoder.decode(new Uint8Array(e.message)));
      const gameIp = e.remoteInfo.address;
      // 如果收到是广播  不作为
      if (this.gameMessage.gameIp && this.gameMessage.gameIp !== gameIp) return;
      // if(Object.keys(message).length){
        console.log("============接收数据================",message)
      // }
      // 心跳记录
      this.lastHeartbeat = Date.now();
      // 心跳清空掉
      this.reconnectTimes = 0;
      // 收到心跳
      if(Object.keys(message).length === 0){
        console.log("==========发送心跳===========")
        this.onceSend({},gameIp);
      }
      
      if(!this.isLine) return;

      // 清除消息C端发送等待重试的消息
      if (message[mKey.backMessageTryId]) {
        const pendingMessages = this.pendingMessages.get(message[mKey.backMessageTryId]);
        if(pendingMessages && pendingMessages.message[mKey.loadingText]){
          this.loadingText === pendingMessages.message[mKey.loadingText] && (this.loadingText = undefined);
          wx.hideLoading();
        }
        this.pendingMessages.delete(message[mKey.backMessageTryId]);
      }
      
      // 处理重复消息
      if (this._isDuplicateMessage(message)) {
        return;
      }

      // 消息版本对比 看是否需要同步数据
      if(!!message[mKey.version]){
          const version = this.computedVersion()
          if(message[mKey.version] < version) return;
          if(message[mKey.version] !== version){
            const param = {
                [mKey.messageType]: sendKey.keep_connect,
                [mKey.jumpMessage]: "游戏数据丢失",
                [mKey.failJumpTo]: 1,
                [mKey.isNeedReply]: 0 // 因为有ok回复
               };
            this.send(param, this.gameMessage.gameIp);
          }
      }

      // 处理需要回复的消息（立即回复）前提是客户端还在
      if (message[mKey.isNeedReply] === 1) {
        this.onceSend({
            [mKey.backMessageType]: message[mKey.messageType],
            [mKey.backMessageTryId]: message[mKey.messageId],
        },gameIp);
      }

      // 离线\上线状态更新
      if (message[mKey.messageType] === sendKey.setLine) {
        // 有人离线
        if(message[mKey.offLine]){
          message[mKey.offLine].forEach(ip => {
            this.setPartOffLine(ip)
          });
        }
        // 有人上线
        if(message[mKey.onLine]){
          message[mKey.onLine].forEach(ip => {
            this.setPartOnLine(ip)
          });
        }
      }

      // 服务端同意加入-外部返回
      if (message[mKey.messageType] === sendKey.client_back_join_ok) {
        const messageIndex = message[mKey.gameMessage].players.findIndex(
          (d) => d.ip === message[mKey.userIp],
        );
        if (messageIndex === -1) {
          getApp().globalData.showToastTitle = "加入房间失败";
          this.pageContext.destroy();
        } else {
          this.gameMessage = message[mKey.gameMessage];
          const { element } = this.getAssignPart(message[mKey.userIp]);
          this.pageContext.setData({modalCole: 0})
          this.ownGameMessage = JSON.parse(JSON.stringify(element));
          // 同步角色和数量
          this.setRoleAndCount()
          // 同步历史数据
          this.historyMessageExtract()
          this.startHeartbeat();
        }
      }

      // 服务端同意加入
      if (message[mKey.messageType] === sendKey.client_join_game_ok) {
        const messageIndex = message[mKey.gameMessage].players.findIndex(
          (d) => d.ip === message[mKey.userIp],
        );
        if (messageIndex === -1) {
          getApp().globalData.showToastTitle = "加入房间失败";
          this.pageContext.destroy();
        } else {
          this.gameMessage = message[mKey.gameMessage];
          const { element } = this.getAssignPart(message[mKey.userIp]);
          this.pageContext.setData({modalCole: 0})
          this.ownGameMessage = JSON.parse(JSON.stringify(element));
          // 告诉他收到了成功了
          this.send({
            [mKey.backMessageType]: sendKey.client_join_game_ok,
            [mKey.backMessageTryId]: message[mKey.messageId],
            [mKey.userIp]: message[mKey.userIp],
          }, this.gameMessage.gameIp);
          // 同步角色和数量
          this.setRoleAndCount()
          // 同步历史数据
          this.historyMessageExtract()
          this.startHeartbeat();
        }
      }

      // 保持连接成功 同步游戏状态
      if (message[mKey.messageType] === sendKey.keep_connect_ok) {
        this.gameMessage = message[mKey.gameMessage];
        const { element } = this.getAssignPart(message[mKey.userIp]);
        this.ownGameMessage = JSON.parse(JSON.stringify(element));
        this.pageContext.setData({ modalCole: 0 })
        // 同步角色和数量
        this.setRoleAndCount()
        // 同步历史数据
        this.historyMessageExtract()
        if (!this.heartbeat) this.startHeartbeat();
      }

      // 服务端暂时切出
      if (message[mKey.messageType] === sendKey.right_back) {
        this.pageContext.setData({ modalCole: 31 })
        this.clearnTimers()
      }

      // 服务端离开后回来了
      if (message[mKey.messageType] === sendKey.client_back_game) {
        this.gameMessage = message[mKey.gameMessage];
        // 同步角色和数量
        this.setRoleAndCount()
        // 同步历史数据
        this.historyMessageExtract()
        this.pageContext.setData({ modalCole: 0 })
        if (!this.heartbeat) this.startHeartbeat();
      }

      //  解散房间
      if (message[mKey.messageType] === sendKey.close_game) {
        this.pageContext.setData({modalCole: 9})
      }
      
      //-------------------------------- back -----------------------------------
      // 退出房间ok （客户端）
      if (message[mKey.backMessageType] === sendKey.client_exit_room) {
        this.pageContext.destroy();
        wx.hideLoading();
      }

      // 后面用共用的 游戏操作逻辑
      this.commonOnMessage(message);
    });
  }

  // 销毁
  destroy() {
    this.resetUDP([9]);
  }
  // 重置UDP
  resetUDP(rule = []) {
    if (!rule.includes(0)) this.UDP.close();
    if (!rule.includes(1)) this.UDP.offMessage();
    if (!rule.includes(2)) this.UDP = null;
    if (!rule.includes(3)) this.messageQueue = []; // 消息队列
    if (!rule.includes(4)) this.lastHeartbeat = 0; // 最后一次心跳时间
    if (!rule.includes(5)) this.pendingMessages.clear();
    if (!rule.includes(7)) this.reconnectTimes = 0; // 消息序列号
    if (!rule.includes(8)) this.clearnTimers(); // 清除定时器
    if (!rule.includes(9)) this.create(); // 重新启动
    if (!rule.includes(10))
      this.gameMessage = JSON.parse(JSON.stringify(config.gameMessage)); // 重置游戏数据
    if (!rule.includes(11)) {
      const pages = getCurrentPages();
      const routeStackLength = pages.length;
      routeStackLength > 1 ? wx.navigateBack({ delta: 2}) : wx.redirectTo({ url: '/pages/home/home' })
    }
  }
  // ----------------------------------------------------------  业务逻辑 -------------------------------------------------------

  // 客户端尝试加入房间 (在房间外面 back 是已经在 然后返回)
  tryJoinRoom(ip,back) {
    this.isLine = true;
    this.gameMessage.gameIp = ip;
    this.gameMessage = this.gameMessage
    const message = {
      [mKey.messageType]: back ? sendKey.client_back_join : sendKey.client_join_game,
      [mKey.userName]: this.userInfo.userName,
      [mKey.userPicture]: this.userInfo.userPicture,
      [mKey.jumpMessage]: "加入失败,请重新获取房间",
      [mKey.loadingText]: "加入中...",
      [mKey.gameIp]: ip,
      [mKey.failJumpTo]: 1,
      [mKey.isNeedReply]: 0  // 因为有ok回复
    };
    this.send(message, ip);
  }

  // 客户端尝试重新连接
  clientTryConnect() {
    this.isLine = true;
    const message = {
      [mKey.messageType]: sendKey.keep_connect,
      [mKey.jumpMessage]: "尝试连接失败",
      [mKey.loadingText]: "连接中...",
      [mKey.failJumpTo]: 1,
      [mKey.isNeedReply]: 0 // 因为有ok回复
    };
    this.send(message, this.gameMessage.gameIp);
    this.pageContext.setData({modalCole: 0})
  }

  // 更新用户信息
  upDataUserInfo() {
    this.userInfo = wx.getStorageSync("userInfo");
    this.onceSend({
      [mKey.messageType]: sendKey.updata_user_info,
      [mKey.userName]: this.userInfo.userName,
      [mKey.userPicture]: this.userInfo.userPicture,
    });
  }
}
